========= loading data =========

m1 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID01.csv')
m2 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID02.csv')
m3 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID03.csv')
m4 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID11.csv')
m5 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID12.csv')
m6 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID13.csv')
m7 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID21.csv')
m8 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID22.csv')
m9 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID23.csv')
m10 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID29.csv')
m11 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID30.csv')
m12 = read.csv('~/Desktop/NCSA/CGManalyzer-datasets/ID31.csv')

training_frame = rbind.data.frame(
                            m1$glucoseValue,
                            m2$glucoseValue,
                            m3$glucoseValue,
                            m4$glucoseValue,
                            m5$glucoseValue,
                            m6$glucoseValue,
                            m7$glucoseValue,
                            m8$glucoseValue,
                            m9$glucoseValue,
                            m10$glucoseValue,
                            m11$glucoseValue,
                            m12$glucoseValue
                            )
training_frame

========= loading packages ===========

require(dtwclust)
Loading required package: dtwclust
Loading required package: proxy

Attaching package: ‘proxy’

The following objects are masked from ‘package:stats’:

    as.dist, dist

The following object is masked from ‘package:base’:

    as.matrix

Loading required package: dtw
Loaded dtw v1.21-3. See ?dtw for help, citation("dtw") for use in publication.

Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
dtwclust:
Setting random number generator to L'Ecuyer-CMRG (see RNGkind()).
To read the included vignettes type: browseVignettes("dtwclust").
See news(package = "dtwclust") after package updates.
require(mcclust)
Loading required package: mcclust
Loading required package: lpSolve
require(ClusterR)
Loading required package: ClusterR
Loading required package: gtools

======== HIERARCHICAL => Raw ===========

clust.hier_raw <- tsclust(training_frame, type = "h", k = 4L, distance = "dtw2", trace=TRUE, control = hierarchical_control(method = "ward.D"))

Calculating distance matrix...
Performing hierarchical clustering...
Extracting centroids...

    Elapsed time is 114.916 seconds.
plot(clust.hier_raw, type="sc")

plot(clust.hier_raw)

t(cbind(training_frame[,0], cluster = clust.hier_raw@cluster))
        1 2 3 4 5 6 7 8 9 10 11 12
cluster 1 1 2 3 3 3 1 4 3  4  4  4
l_hier <- clust.hier_raw@cluster
m_hier <- c(1,1,1,3,3,3,2,2,2,4,4,4)
plot(range(1:12),range(1:4), type='n')
points(m_hier, col='red')
lines(l_hier, col='green')

predict(clust.hier_raw,newdata=unlist(m3$glucoseValue))
[1] 2
predict(clust.hier_raw,newdata=unlist(m6$glucoseValue))
[1] 3
predict(clust.hier_raw,newdata=unlist(m9$glucoseValue))
[1] 3
predict(clust.hier_raw,newdata=unlist(m12$glucoseValue))
[1] 4
index_hier_raw=arandi(l_hier,m_hier)
unadjusted_hier_raw=arandi(l_hier,m_hier,adjust=FALSE)
index_hier_raw
[1] 0.3966245
unadjusted_hier_raw
[1] 0.8030303

=========== Partitional => Raw ===========

clust.par_raw <- tsclust(training_frame, type = "partitional", k = 4L, distance = "dtw2", trace=TRUE)

    Precomputing distance matrix...

Iteration 1: Changes / Distsum = 12 / 659.1767
Iteration 2: Changes / Distsum =  1 / 489.335
Iteration 3: Changes / Distsum =  0 / 489.335

    Elapsed time is 62.314 seconds.
plot(clust.par_raw, type="sc")

t(cbind(training_frame[,0], cluster = clust.par_raw@cluster))
        [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
cluster    3    3    4    2    3    2    3    1    2     1     1     1
l_par <- clust.par_raw@cluster
m_par <- c(4,4,4,1,1,1,2,2,2,3,3,3)
plot(range(1:12),range(1:4), type='n')
points(m_par, col='red')
lines(l_par, col='green')

predict(clust.par_raw,newdata=unlist(m3$glucoseValue))
[1] 4
predict(clust.par_raw,newdata=unlist(m6$glucoseValue))
[1] 1
predict(clust.par_raw,newdata=unlist(m9$glucoseValue))
[1] 2
predict(clust.par_raw,newdata=unlist(m12$glucoseValue))
[1] 1
index_par_raw=arandi(l_par,m_par)
unadjusted_par_raw=arandi(l_par,m_par,adjust=FALSE)
index_par_raw
[1] 0.2109705
unadjusted_par_raw
[1] 0.7424242

========== K Means => Raw ===========

kmeans_cluster_raw = KMeans_rcpp(training_frame,clusters=4)
l_kmeans_raw = kmeans_cluster_raw$cluster
l_kmeans_raw
 [1] 3 4 1 2 2 4 3 4 2 4 4 4
m_kmeans_raw <- c(1,1,1,2,2,2,3,3,3,4,4,4)

index_kmeans_raw = arandi(l_kmeans_raw,m_kmeans_raw)
unadjusted_kmeans_raw = arandi(l_kmeans_raw,m_kmeans_raw,adjust = TRUE)
index_kmeans_raw
[1] 0.04528302
unadjusted_kmeans_raw
[1] 0.04528302

========== Linear Scaling ============

linearScaling = function(data){
  scaled = c()
  for (i in 1:length(data)) {
    scaled[i] = (data[i]-min(data))/(max(data)-min(data))
    #print(scaled[i])
  }
  return(scaled)
}


training_frame_scaled<-rbind.data.frame(
  linearScaling(m1$glucoseValue),
  linearScaling(m2$glucoseValue),
  linearScaling(m3$glucoseValue),
  linearScaling(m4$glucoseValue),
  linearScaling(m5$glucoseValue),
  linearScaling(m6$glucoseValue),
  linearScaling(m7$glucoseValue),
  linearScaling(m8$glucoseValue),
  linearScaling(m9$glucoseValue),
  linearScaling(m10$glucoseValue),
  linearScaling(m11$glucoseValue),
  linearScaling(m12$glucoseValue)
)
training_frame_scaled

======== HIERARCHICAL => Scaled ===========

clust.hier_scaled <- tsclust(training_frame_scaled, type = "h", k = 4L, distance = "dtw2", trace=TRUE, control = hierarchical_control(method = "ward.D"))

Calculating distance matrix...
Performing hierarchical clustering...
Extracting centroids...

    Elapsed time is 100.308 seconds.
plot(clust.hier_scaled, type="sc")

plot(clust.hier_scaled)

t(cbind(training_frame_scaled[,0], cluster = clust.hier_scaled@cluster))
        1 2 3 4 5 6 7 8 9 10 11 12
cluster 1 2 3 4 2 4 2 4 3  4  4  4
l_hier <- clust.hier_scaled@cluster
m_hier <- c(1,1,1,3,3,3,2,2,2,4,4,4)
plot(range(1:12),range(1:4), type='n')
points(m_hier, col='red')
lines(l_hier, col='green')

predict(clust.hier_scaled,newdata=unlist(linearScaling(m3$glucoseValue)))
[1] 3
predict(clust.hier_scaled,newdata=unlist(linearScaling(m6$glucoseValue)))
[1] 3
predict(clust.hier_scaled,newdata=unlist(linearScaling(m9$glucoseValue)))
[1] 3
predict(clust.hier_scaled,newdata=unlist(linearScaling(m12$glucoseValue)))
[1] 3
index_hier_scaled=arandi(l_hier,m_hier)
unadjusted_hier_scaled=arandi(l_hier,m_hier,adjust=FALSE)
index_hier_scaled
[1] 0.04528302
unadjusted_hier_scaled
[1] 0.6515152

=========== Partitional => Scaled ===========

clust.par_scaled <- tsclust(training_frame_scaled, type = "partitional", k = 4L, distance = "dtw2", trace=TRUE)

    Precomputing distance matrix...

Iteration 1: Changes / Distsum = 12 / 37.12822
Iteration 2: Changes / Distsum = 1 / 32.85783
Iteration 3: Changes / Distsum = 0 / 32.85783

    Elapsed time is 54.819 seconds.
plot(clust.par_scaled, type="sc")

t(cbind(training_frame_scaled[,0], cluster = clust.par_scaled@cluster))
        [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
cluster    3    2    4    1    2    1    2    1    2     1     4     4
l_par <- clust.par_scaled@cluster
m_par <- c(3,3,3,1,1,1,2,2,2,4,4,4)
plot(range(1:12),range(1:4), type='n')
points(m_par, col='red')
lines(l_par, col='green')

predict(clust.par_raw,newdata=unlist(linearScaling(m3$glucoseValue)))
[1] 1
predict(clust.par_raw,newdata=unlist(linearScaling(m6$glucoseValue)))
[1] 1
predict(clust.par_raw,newdata=unlist(linearScaling(m9$glucoseValue)))
[1] 1
predict(clust.par_raw,newdata=unlist(linearScaling(m12$glucoseValue)))
[1] 1
index_par_scaled=arandi(l_par,m_par)
unadjusted_par_scaled=arandi(l_par,m_par,adjust=FALSE)
index_par_scaled
[1] 0.02531646
unadjusted_par_scaled
[1] 0.6818182

========== K Means => Scaled ===========

kmeans_cluster_scaled = KMeans_rcpp(training_frame_scaled,clusters=4)
l_kmeans_scaled = kmeans_cluster_scaled$cluster
l_kmeans_scaled
 [1] 3 2 3 1 2 1 3 4 4 2 1 4
m_kmeans_scaled <- c(3,3,3,2,2,2,4,4,4,1,1,1)

index_kmeans_scaled = arandi(l_kmeans_scaled,m_kmeans_scaled)
unadjusted_kmeans_scaled = arandi(l_kmeans_scaled,m_kmeans_scaled,adjust = TRUE)
index_kmeans_scaled
[1] 0.08333333
unadjusted_kmeans_scaled
[1] 0.08333333

=========== Z Score Normalization =============

training_frame_zscore<-rbind.data.frame(
  zscore(m1$glucoseValue),
  zscore(m2$glucoseValue),
  zscore(m3$glucoseValue),
  zscore(m4$glucoseValue),
  zscore(m5$glucoseValue),
  zscore(m6$glucoseValue),
  zscore(m7$glucoseValue),
  zscore(m8$glucoseValue),
  zscore(m9$glucoseValue),
  zscore(m10$glucoseValue),
  zscore(m11$glucoseValue),
  zscore(m12$glucoseValue)
)
training_frame_zscore

======== HIERARCHICAL => Z score ===========

clust.hier_zscore <- tsclust(training_frame_zscore, type = "h", k = 4L, distance = "dtw2", trace=TRUE, control = hierarchical_control(method = "ward.D"))

Calculating distance matrix...
Performing hierarchical clustering...
Extracting centroids...

    Elapsed time is 139.071 seconds.
plot(clust.hier_zscore, type="sc")

plot(clust.hier_zscore)

t(cbind(training_frame_zscore[,0], cluster = clust.hier_zscore@cluster))
        1 2 3 4 5 6 7 8 9 10 11 12
cluster 1 1 2 3 1 3 1 3 4  4  4  2
l_hier <- clust.hier_zscore@cluster
m_hier <- c(1,1,1,3,3,3,2,2,2,4,4,4)
plot(range(1:12),range(1:4), type='n')
points(m_hier, col='red')
lines(l_hier, col='green')

predict(clust.hier_zscore,newdata=unlist(zscore(m3$glucoseValue)))
[1] 2
predict(clust.hier_zscore,newdata=unlist(zscore(m6$glucoseValue)))
[1] 2
predict(clust.hier_zscore,newdata=unlist(zscore(m9$glucoseValue)))
[1] 4
predict(clust.hier_zscore,newdata=unlist(zscore(m12$glucoseValue)))
[1] 2
index_hier_zscore=arandi(l_hier,m_hier)
unadjusted_hier_zscore=arandi(l_hier,m_hier,adjust=FALSE)
index_hier_zscore
[1] 0.06278027
unadjusted_hier_zscore
[1] 0.7121212

=========== Partitional => Zscore ===========

clust.par_zscore <- tsclust(training_frame_zscore, type = "partitional", k = 4L, distance = "dtw2", trace=TRUE)

    Precomputing distance matrix...

Iteration 1: Changes / Distsum = 12 / 199.0184
Iteration 2: Changes / Distsum = 2 / 192.3014
Iteration 3: Changes / Distsum = 1 / 161.7827
Iteration 4: Changes / Distsum = 0 / 161.7827

    Elapsed time is 69.23 seconds.
plot(clust.par_zscore, type="sc")

t(cbind(training_frame_zscore[,0], cluster = clust.par_zscore@cluster))
        [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
cluster    3    2    1    1    2    1    2    1    2     4     4     1
l_par <- clust.par_zscore@cluster
m_par <- c(3,3,3,1,1,1,2,2,2,4,4,4)
plot(range(1:12),range(1:4), type='n')
points(m_par, col='red')
lines(l_par, col='green')

predict(clust.par_zscore,newdata=unlist(zscore(m3$glucoseValue)))
[1] 1
predict(clust.par_zscore,newdata=unlist(zscore(m6$glucoseValue)))
[1] 2
predict(clust.par_zscore,newdata=unlist(zscore(m9$glucoseValue)))
[1] 2
predict(clust.par_zscore,newdata=unlist(zscore(m12$glucoseValue)))
[1] 1
index_par_zscore=arandi(l_par,m_par)
unadjusted_par_zscore=arandi(l_par,m_par,adjust=FALSE)
index_par_zscore
[1] -0.007968127
unadjusted_par_zscore
[1] 0.6515152

========== K Means => Zscore ===========

kmeans_cluster_zscore = KMeans_rcpp(training_frame_zscore,clusters=4)
l_kmeans_zscore = kmeans_cluster_zscore$cluster
l_kmeans_zscore
 [1] 3 2 2 1 2 1 3 4 2 2 1 4
m_kmeans_zscore <- c(2,2,2,3,3,3,4,4,4,1,1,1)
index_kmeans_zscore = arandi(l_kmeans_scaled,m_kmeans_scaled)
unadjusted_kmeans_zscore = arandi(l_kmeans_scaled,m_kmeans_scaled,adjust = TRUE)
index_kmeans_zscore
[1] 0.08333333
unadjusted_kmeans_zscore
[1] 0.08333333

=========Plotting============

colors = c('red','blue','green')
c_types = c('Hierarchical Clustering','Partitional Clustering','K Means Clustering')
t_types = c('Raw', 'Min-Max Scaling','Z-Score Normalization')
index_all = c(index_hier_raw,index_hier_scaled,index_hier_zscore, index_par_raw, index_par_scaled, index_par_zscore, index_kmeans_raw, index_kmeans_scaled, index_kmeans_zscore)

plot(index_all,xaxt='n',type='h', col = colors, xlab="Clustering Methods", ylab="Adjusted Random Index", main='Adjusted Random Index: Hierarchical, Partitional & K-Means Clustering', lwd=4)
axis(1, at=seq(2,9,by=3), labels=c_types[1:3])
legend("topright",t_types, col = colors, title = 'Transformation Types', lwd=2,cex=.75)

unadjusted_all = c(unadjusted_hier_raw, unadjusted_hier_scaled, unadjusted_hier_zscore, unadjusted_par_raw, unadjusted_par_scaled, unadjusted_par_zscore, unadjusted_kmeans_raw, unadjusted_kmeans_scaled, unadjusted_kmeans_zscore)

plot(unadjusted_all,xaxt='n',type='h', col = colors, xlab="Clustering Methods", ylab="Unadjusted Random Index", main='Unadjusted Index: Hierarchical, Partitional & K-Means Clustering', lwd=4)
axis(1, at=seq(2,9,by=3), labels=c_types[1:3])
legend("topright",t_types, col = colors, title = 'Transformation Types', lwd=2, cex=0.75)

LS0tCnRpdGxlOiAiY2dtYW5hbHlzZXJEVFciCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KPT09PT09PT09IGxvYWRpbmcgZGF0YSA9PT09PT09PT0KYGBge3J9Cm0xID0gcmVhZC5jc3YoJ34vRGVza3RvcC9OQ1NBL0NHTWFuYWx5emVyLWRhdGFzZXRzL0lEMDEuY3N2JykKbTIgPSByZWFkLmNzdignfi9EZXNrdG9wL05DU0EvQ0dNYW5hbHl6ZXItZGF0YXNldHMvSUQwMi5jc3YnKQptMyA9IHJlYWQuY3N2KCd+L0Rlc2t0b3AvTkNTQS9DR01hbmFseXplci1kYXRhc2V0cy9JRDAzLmNzdicpCm00ID0gcmVhZC5jc3YoJ34vRGVza3RvcC9OQ1NBL0NHTWFuYWx5emVyLWRhdGFzZXRzL0lEMTEuY3N2JykKbTUgPSByZWFkLmNzdignfi9EZXNrdG9wL05DU0EvQ0dNYW5hbHl6ZXItZGF0YXNldHMvSUQxMi5jc3YnKQptNiA9IHJlYWQuY3N2KCd+L0Rlc2t0b3AvTkNTQS9DR01hbmFseXplci1kYXRhc2V0cy9JRDEzLmNzdicpCm03ID0gcmVhZC5jc3YoJ34vRGVza3RvcC9OQ1NBL0NHTWFuYWx5emVyLWRhdGFzZXRzL0lEMjEuY3N2JykKbTggPSByZWFkLmNzdignfi9EZXNrdG9wL05DU0EvQ0dNYW5hbHl6ZXItZGF0YXNldHMvSUQyMi5jc3YnKQptOSA9IHJlYWQuY3N2KCd+L0Rlc2t0b3AvTkNTQS9DR01hbmFseXplci1kYXRhc2V0cy9JRDIzLmNzdicpCm0xMCA9IHJlYWQuY3N2KCd+L0Rlc2t0b3AvTkNTQS9DR01hbmFseXplci1kYXRhc2V0cy9JRDI5LmNzdicpCm0xMSA9IHJlYWQuY3N2KCd+L0Rlc2t0b3AvTkNTQS9DR01hbmFseXplci1kYXRhc2V0cy9JRDMwLmNzdicpCm0xMiA9IHJlYWQuY3N2KCd+L0Rlc2t0b3AvTkNTQS9DR01hbmFseXplci1kYXRhc2V0cy9JRDMxLmNzdicpCgp0cmFpbmluZ19mcmFtZSA9IHJiaW5kLmRhdGEuZnJhbWUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtMSRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtMiRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtMyRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtNCRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtNSRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtNiRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtNyRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtOCRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtOSRnbHVjb3NlVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtMTAkZ2x1Y29zZVZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbTExJGdsdWNvc2VWYWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG0xMiRnbHVjb3NlVmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKdHJhaW5pbmdfZnJhbWUKYGBgCgo9PT09PT09PT0gbG9hZGluZyBwYWNrYWdlcyA9PT09PT09PT09PQpgYGB7cn0KcmVxdWlyZShkdHdjbHVzdCkKcmVxdWlyZShtY2NsdXN0KQpyZXF1aXJlKENsdXN0ZXJSKQpgYGAKCgoKCj09PT09PT09IEhJRVJBUkNISUNBTCA9PiBSYXcgPT09PT09PT09PT0KYGBge3J9CmNsdXN0LmhpZXJfcmF3IDwtIHRzY2x1c3QodHJhaW5pbmdfZnJhbWUsIHR5cGUgPSAiaCIsIGsgPSA0TCwgZGlzdGFuY2UgPSAiZHR3MiIsIHRyYWNlPVRSVUUsIGNvbnRyb2wgPSBoaWVyYXJjaGljYWxfY29udHJvbChtZXRob2QgPSAid2FyZC5EIikpCgpwbG90KGNsdXN0LmhpZXJfcmF3LCB0eXBlPSJzYyIpCmBgYAoKYGBge3J9CnBsb3QoY2x1c3QuaGllcl9yYXcpCmBgYAoKCmBgYHtyfQp0KGNiaW5kKHRyYWluaW5nX2ZyYW1lWywwXSwgY2x1c3RlciA9IGNsdXN0LmhpZXJfcmF3QGNsdXN0ZXIpKQpgYGAKCmBgYHtyfQpsX2hpZXIgPC0gY2x1c3QuaGllcl9yYXdAY2x1c3RlcgptX2hpZXIgPC0gYygxLDEsMSwzLDMsMywyLDIsMiw0LDQsNCkKYGBgCgpgYGB7cn0KcGxvdChyYW5nZSgxOjEyKSxyYW5nZSgxOjQpLCB0eXBlPSduJykKcG9pbnRzKG1faGllciwgY29sPSdyZWQnKQpsaW5lcyhsX2hpZXIsIGNvbD0nZ3JlZW4nKQpgYGAKYGBge3J9CnByZWRpY3QoY2x1c3QuaGllcl9yYXcsbmV3ZGF0YT11bmxpc3QobTMkZ2x1Y29zZVZhbHVlKSkKcHJlZGljdChjbHVzdC5oaWVyX3JhdyxuZXdkYXRhPXVubGlzdChtNiRnbHVjb3NlVmFsdWUpKQpwcmVkaWN0KGNsdXN0LmhpZXJfcmF3LG5ld2RhdGE9dW5saXN0KG05JGdsdWNvc2VWYWx1ZSkpCnByZWRpY3QoY2x1c3QuaGllcl9yYXcsbmV3ZGF0YT11bmxpc3QobTEyJGdsdWNvc2VWYWx1ZSkpCmBgYApgYGB7cn0KaW5kZXhfaGllcl9yYXc9YXJhbmRpKGxfaGllcixtX2hpZXIpCnVuYWRqdXN0ZWRfaGllcl9yYXc9YXJhbmRpKGxfaGllcixtX2hpZXIsYWRqdXN0PUZBTFNFKQppbmRleF9oaWVyX3Jhdwp1bmFkanVzdGVkX2hpZXJfcmF3CmBgYAoKCgo9PT09PT09PT09PSBQYXJ0aXRpb25hbCA9PiBSYXcgPT09PT09PT09PT0KYGBge3J9CmNsdXN0LnBhcl9yYXcgPC0gdHNjbHVzdCh0cmFpbmluZ19mcmFtZSwgdHlwZSA9ICJwYXJ0aXRpb25hbCIsIGsgPSA0TCwgZGlzdGFuY2UgPSAiZHR3MiIsIHRyYWNlPVRSVUUpCgpwbG90KGNsdXN0LnBhcl9yYXcsIHR5cGU9InNjIikKYGBgCgoKCgpgYGB7cn0KdChjYmluZCh0cmFpbmluZ19mcmFtZVssMF0sIGNsdXN0ZXIgPSBjbHVzdC5wYXJfcmF3QGNsdXN0ZXIpKQpgYGAKCmBgYHtyfQpsX3BhciA8LSBjbHVzdC5wYXJfcmF3QGNsdXN0ZXIKbV9wYXIgPC0gYyg0LDQsNCwxLDEsMSwyLDIsMiwzLDMsMykKYGBgCgpgYGB7cn0KcGxvdChyYW5nZSgxOjEyKSxyYW5nZSgxOjQpLCB0eXBlPSduJykKcG9pbnRzKG1fcGFyLCBjb2w9J3JlZCcpCmxpbmVzKGxfcGFyLCBjb2w9J2dyZWVuJykKYGBgCgpgYGB7cn0KcHJlZGljdChjbHVzdC5wYXJfcmF3LG5ld2RhdGE9dW5saXN0KG0zJGdsdWNvc2VWYWx1ZSkpCnByZWRpY3QoY2x1c3QucGFyX3JhdyxuZXdkYXRhPXVubGlzdChtNiRnbHVjb3NlVmFsdWUpKQpwcmVkaWN0KGNsdXN0LnBhcl9yYXcsbmV3ZGF0YT11bmxpc3QobTkkZ2x1Y29zZVZhbHVlKSkKcHJlZGljdChjbHVzdC5wYXJfcmF3LG5ld2RhdGE9dW5saXN0KG0xMiRnbHVjb3NlVmFsdWUpKQpgYGAKCmBgYHtyfQppbmRleF9wYXJfcmF3PWFyYW5kaShsX3BhcixtX3BhcikKdW5hZGp1c3RlZF9wYXJfcmF3PWFyYW5kaShsX3BhcixtX3BhcixhZGp1c3Q9RkFMU0UpCmluZGV4X3Bhcl9yYXcKdW5hZGp1c3RlZF9wYXJfcmF3CmBgYAoKCgoKCgoKCj09PT09PT09PT0gSyBNZWFucyA9PiBSYXcgPT09PT09PT09PT0KYGBge3J9CmttZWFuc19jbHVzdGVyX3JhdyA9IEtNZWFuc19yY3BwKHRyYWluaW5nX2ZyYW1lLGNsdXN0ZXJzPTQpCmxfa21lYW5zX3JhdyA9IGttZWFuc19jbHVzdGVyX3JhdyRjbHVzdGVyCmxfa21lYW5zX3JhdwptX2ttZWFuc19yYXcgPC0gYygxLDEsMSwyLDIsMiwzLDMsMyw0LDQsNCkKYGBgCgpgYGB7cn0KCmluZGV4X2ttZWFuc19yYXcgPSBhcmFuZGkobF9rbWVhbnNfcmF3LG1fa21lYW5zX3JhdykKdW5hZGp1c3RlZF9rbWVhbnNfcmF3ID0gYXJhbmRpKGxfa21lYW5zX3JhdyxtX2ttZWFuc19yYXcsYWRqdXN0ID0gVFJVRSkKaW5kZXhfa21lYW5zX3Jhdwp1bmFkanVzdGVkX2ttZWFuc19yYXcKYGBgCgoKPT09PT09PT09PSBMaW5lYXIgU2NhbGluZyA9PT09PT09PT09PT0KYGBge3J9CmxpbmVhclNjYWxpbmcgPSBmdW5jdGlvbihkYXRhKXsKICBzY2FsZWQgPSBjKCkKICBmb3IgKGkgaW4gMTpsZW5ndGgoZGF0YSkpIHsKICAgIHNjYWxlZFtpXSA9IChkYXRhW2ldLW1pbihkYXRhKSkvKG1heChkYXRhKS1taW4oZGF0YSkpCiAgICAjcHJpbnQoc2NhbGVkW2ldKQogIH0KICByZXR1cm4oc2NhbGVkKQp9CgoKdHJhaW5pbmdfZnJhbWVfc2NhbGVkPC1yYmluZC5kYXRhLmZyYW1lKAogIGxpbmVhclNjYWxpbmcobTEkZ2x1Y29zZVZhbHVlKSwKICBsaW5lYXJTY2FsaW5nKG0yJGdsdWNvc2VWYWx1ZSksCiAgbGluZWFyU2NhbGluZyhtMyRnbHVjb3NlVmFsdWUpLAogIGxpbmVhclNjYWxpbmcobTQkZ2x1Y29zZVZhbHVlKSwKICBsaW5lYXJTY2FsaW5nKG01JGdsdWNvc2VWYWx1ZSksCiAgbGluZWFyU2NhbGluZyhtNiRnbHVjb3NlVmFsdWUpLAogIGxpbmVhclNjYWxpbmcobTckZ2x1Y29zZVZhbHVlKSwKICBsaW5lYXJTY2FsaW5nKG04JGdsdWNvc2VWYWx1ZSksCiAgbGluZWFyU2NhbGluZyhtOSRnbHVjb3NlVmFsdWUpLAogIGxpbmVhclNjYWxpbmcobTEwJGdsdWNvc2VWYWx1ZSksCiAgbGluZWFyU2NhbGluZyhtMTEkZ2x1Y29zZVZhbHVlKSwKICBsaW5lYXJTY2FsaW5nKG0xMiRnbHVjb3NlVmFsdWUpCikKdHJhaW5pbmdfZnJhbWVfc2NhbGVkCmBgYAoKCgo9PT09PT09PSBISUVSQVJDSElDQUwgPT4gU2NhbGVkID09PT09PT09PT09CmBgYHtyfQpjbHVzdC5oaWVyX3NjYWxlZCA8LSB0c2NsdXN0KHRyYWluaW5nX2ZyYW1lX3NjYWxlZCwgdHlwZSA9ICJoIiwgayA9IDRMLCBkaXN0YW5jZSA9ICJkdHcyIiwgdHJhY2U9VFJVRSwgY29udHJvbCA9IGhpZXJhcmNoaWNhbF9jb250cm9sKG1ldGhvZCA9ICJ3YXJkLkQiKSkKCnBsb3QoY2x1c3QuaGllcl9zY2FsZWQsIHR5cGU9InNjIikKYGBgCgpgYGB7cn0KcGxvdChjbHVzdC5oaWVyX3NjYWxlZCkKYGBgCgoKYGBge3J9CnQoY2JpbmQodHJhaW5pbmdfZnJhbWVfc2NhbGVkWywwXSwgY2x1c3RlciA9IGNsdXN0LmhpZXJfc2NhbGVkQGNsdXN0ZXIpKQpgYGAKCmBgYHtyfQpsX2hpZXIgPC0gY2x1c3QuaGllcl9zY2FsZWRAY2x1c3RlcgptX2hpZXIgPC0gYygxLDEsMSwzLDMsMywyLDIsMiw0LDQsNCkKYGBgCgpgYGB7cn0KcGxvdChyYW5nZSgxOjEyKSxyYW5nZSgxOjQpLCB0eXBlPSduJykKcG9pbnRzKG1faGllciwgY29sPSdyZWQnKQpsaW5lcyhsX2hpZXIsIGNvbD0nZ3JlZW4nKQpgYGAKYGBge3J9CnByZWRpY3QoY2x1c3QuaGllcl9zY2FsZWQsbmV3ZGF0YT11bmxpc3QobGluZWFyU2NhbGluZyhtMyRnbHVjb3NlVmFsdWUpKSkKcHJlZGljdChjbHVzdC5oaWVyX3NjYWxlZCxuZXdkYXRhPXVubGlzdChsaW5lYXJTY2FsaW5nKG02JGdsdWNvc2VWYWx1ZSkpKQpwcmVkaWN0KGNsdXN0LmhpZXJfc2NhbGVkLG5ld2RhdGE9dW5saXN0KGxpbmVhclNjYWxpbmcobTkkZ2x1Y29zZVZhbHVlKSkpCnByZWRpY3QoY2x1c3QuaGllcl9zY2FsZWQsbmV3ZGF0YT11bmxpc3QobGluZWFyU2NhbGluZyhtMTIkZ2x1Y29zZVZhbHVlKSkpCmBgYApgYGB7cn0KaW5kZXhfaGllcl9zY2FsZWQ9YXJhbmRpKGxfaGllcixtX2hpZXIpCnVuYWRqdXN0ZWRfaGllcl9zY2FsZWQ9YXJhbmRpKGxfaGllcixtX2hpZXIsYWRqdXN0PUZBTFNFKQppbmRleF9oaWVyX3NjYWxlZAp1bmFkanVzdGVkX2hpZXJfc2NhbGVkCmBgYAoKCgo9PT09PT09PT09PSBQYXJ0aXRpb25hbCA9PiBTY2FsZWQgPT09PT09PT09PT0KCmBgYHtyfQpjbHVzdC5wYXJfc2NhbGVkIDwtIHRzY2x1c3QodHJhaW5pbmdfZnJhbWVfc2NhbGVkLCB0eXBlID0gInBhcnRpdGlvbmFsIiwgayA9IDRMLCBkaXN0YW5jZSA9ICJkdHcyIiwgdHJhY2U9VFJVRSkKCnBsb3QoY2x1c3QucGFyX3NjYWxlZCwgdHlwZT0ic2MiKQpgYGAKCgoKCmBgYHtyfQp0KGNiaW5kKHRyYWluaW5nX2ZyYW1lX3NjYWxlZFssMF0sIGNsdXN0ZXIgPSBjbHVzdC5wYXJfc2NhbGVkQGNsdXN0ZXIpKQpgYGAKCmBgYHtyfQpsX3BhciA8LSBjbHVzdC5wYXJfc2NhbGVkQGNsdXN0ZXIKbV9wYXIgPC0gYygzLDMsMywxLDEsMSwyLDIsMiw0LDQsNCkKYGBgCgpgYGB7cn0KcGxvdChyYW5nZSgxOjEyKSxyYW5nZSgxOjQpLCB0eXBlPSduJykKcG9pbnRzKG1fcGFyLCBjb2w9J3JlZCcpCmxpbmVzKGxfcGFyLCBjb2w9J2dyZWVuJykKYGBgCgpgYGB7cn0KcHJlZGljdChjbHVzdC5wYXJfcmF3LG5ld2RhdGE9dW5saXN0KGxpbmVhclNjYWxpbmcobTMkZ2x1Y29zZVZhbHVlKSkpCnByZWRpY3QoY2x1c3QucGFyX3JhdyxuZXdkYXRhPXVubGlzdChsaW5lYXJTY2FsaW5nKG02JGdsdWNvc2VWYWx1ZSkpKQpwcmVkaWN0KGNsdXN0LnBhcl9yYXcsbmV3ZGF0YT11bmxpc3QobGluZWFyU2NhbGluZyhtOSRnbHVjb3NlVmFsdWUpKSkKcHJlZGljdChjbHVzdC5wYXJfcmF3LG5ld2RhdGE9dW5saXN0KGxpbmVhclNjYWxpbmcobTEyJGdsdWNvc2VWYWx1ZSkpKQpgYGAKCmBgYHtyfQppbmRleF9wYXJfc2NhbGVkPWFyYW5kaShsX3BhcixtX3BhcikKdW5hZGp1c3RlZF9wYXJfc2NhbGVkPWFyYW5kaShsX3BhcixtX3BhcixhZGp1c3Q9RkFMU0UpCmluZGV4X3Bhcl9zY2FsZWQKdW5hZGp1c3RlZF9wYXJfc2NhbGVkCmBgYAoKCj09PT09PT09PT0gSyBNZWFucyA9PiBTY2FsZWQgPT09PT09PT09PT0KYGBge3J9CmttZWFuc19jbHVzdGVyX3NjYWxlZCA9IEtNZWFuc19yY3BwKHRyYWluaW5nX2ZyYW1lX3NjYWxlZCxjbHVzdGVycz00KQpsX2ttZWFuc19zY2FsZWQgPSBrbWVhbnNfY2x1c3Rlcl9zY2FsZWQkY2x1c3RlcgpsX2ttZWFuc19zY2FsZWQKbV9rbWVhbnNfc2NhbGVkIDwtIGMoMywzLDMsMiwyLDIsNCw0LDQsMSwxLDEpCmBgYAoKYGBge3J9CgppbmRleF9rbWVhbnNfc2NhbGVkID0gYXJhbmRpKGxfa21lYW5zX3NjYWxlZCxtX2ttZWFuc19zY2FsZWQpCnVuYWRqdXN0ZWRfa21lYW5zX3NjYWxlZCA9IGFyYW5kaShsX2ttZWFuc19zY2FsZWQsbV9rbWVhbnNfc2NhbGVkLGFkanVzdCA9IFRSVUUpCmluZGV4X2ttZWFuc19zY2FsZWQKdW5hZGp1c3RlZF9rbWVhbnNfc2NhbGVkCmBgYAoKCgoKCgoKCj09PT09PT09PT09IFogU2NvcmUgTm9ybWFsaXphdGlvbiA9PT09PT09PT09PT09CmBgYHtyfQp0cmFpbmluZ19mcmFtZV96c2NvcmU8LXJiaW5kLmRhdGEuZnJhbWUoCiAgenNjb3JlKG0xJGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG0yJGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG0zJGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG00JGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG01JGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG02JGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG03JGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG04JGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG05JGdsdWNvc2VWYWx1ZSksCiAgenNjb3JlKG0xMCRnbHVjb3NlVmFsdWUpLAogIHpzY29yZShtMTEkZ2x1Y29zZVZhbHVlKSwKICB6c2NvcmUobTEyJGdsdWNvc2VWYWx1ZSkKKQp0cmFpbmluZ19mcmFtZV96c2NvcmUKYGBgCgoKPT09PT09PT0gSElFUkFSQ0hJQ0FMID0+IFogc2NvcmUgPT09PT09PT09PT0KYGBge3J9CmNsdXN0LmhpZXJfenNjb3JlIDwtIHRzY2x1c3QodHJhaW5pbmdfZnJhbWVfenNjb3JlLCB0eXBlID0gImgiLCBrID0gNEwsIGRpc3RhbmNlID0gImR0dzIiLCB0cmFjZT1UUlVFLCBjb250cm9sID0gaGllcmFyY2hpY2FsX2NvbnRyb2wobWV0aG9kID0gIndhcmQuRCIpKQoKcGxvdChjbHVzdC5oaWVyX3pzY29yZSwgdHlwZT0ic2MiKQpgYGAKCmBgYHtyfQpwbG90KGNsdXN0LmhpZXJfenNjb3JlKQpgYGAKCgpgYGB7cn0KdChjYmluZCh0cmFpbmluZ19mcmFtZV96c2NvcmVbLDBdLCBjbHVzdGVyID0gY2x1c3QuaGllcl96c2NvcmVAY2x1c3RlcikpCmBgYAoKYGBge3J9CmxfaGllciA8LSBjbHVzdC5oaWVyX3pzY29yZUBjbHVzdGVyCm1faGllciA8LSBjKDEsMSwxLDMsMywzLDIsMiwyLDQsNCw0KQpgYGAKCmBgYHtyfQpwbG90KHJhbmdlKDE6MTIpLHJhbmdlKDE6NCksIHR5cGU9J24nKQpwb2ludHMobV9oaWVyLCBjb2w9J3JlZCcpCmxpbmVzKGxfaGllciwgY29sPSdncmVlbicpCmBgYApgYGB7cn0KcHJlZGljdChjbHVzdC5oaWVyX3pzY29yZSxuZXdkYXRhPXVubGlzdCh6c2NvcmUobTMkZ2x1Y29zZVZhbHVlKSkpCnByZWRpY3QoY2x1c3QuaGllcl96c2NvcmUsbmV3ZGF0YT11bmxpc3QoenNjb3JlKG02JGdsdWNvc2VWYWx1ZSkpKQpwcmVkaWN0KGNsdXN0LmhpZXJfenNjb3JlLG5ld2RhdGE9dW5saXN0KHpzY29yZShtOSRnbHVjb3NlVmFsdWUpKSkKcHJlZGljdChjbHVzdC5oaWVyX3pzY29yZSxuZXdkYXRhPXVubGlzdCh6c2NvcmUobTEyJGdsdWNvc2VWYWx1ZSkpKQpgYGAKYGBge3J9CmluZGV4X2hpZXJfenNjb3JlPWFyYW5kaShsX2hpZXIsbV9oaWVyKQp1bmFkanVzdGVkX2hpZXJfenNjb3JlPWFyYW5kaShsX2hpZXIsbV9oaWVyLGFkanVzdD1GQUxTRSkKaW5kZXhfaGllcl96c2NvcmUKdW5hZGp1c3RlZF9oaWVyX3pzY29yZQpgYGAKCgoKPT09PT09PT09PT0gUGFydGl0aW9uYWwgPT4gWnNjb3JlID09PT09PT09PT09CgpgYGB7cn0KY2x1c3QucGFyX3pzY29yZSA8LSB0c2NsdXN0KHRyYWluaW5nX2ZyYW1lX3pzY29yZSwgdHlwZSA9ICJwYXJ0aXRpb25hbCIsIGsgPSA0TCwgZGlzdGFuY2UgPSAiZHR3MiIsIHRyYWNlPVRSVUUpCgpwbG90KGNsdXN0LnBhcl96c2NvcmUsIHR5cGU9InNjIikKYGBgCgoKCmBgYHtyfQp0KGNiaW5kKHRyYWluaW5nX2ZyYW1lX3pzY29yZVssMF0sIGNsdXN0ZXIgPSBjbHVzdC5wYXJfenNjb3JlQGNsdXN0ZXIpKQpgYGAKCmBgYHtyfQpsX3BhciA8LSBjbHVzdC5wYXJfenNjb3JlQGNsdXN0ZXIKbV9wYXIgPC0gYygzLDMsMywxLDEsMSwyLDIsMiw0LDQsNCkKYGBgCgpgYGB7cn0KcGxvdChyYW5nZSgxOjEyKSxyYW5nZSgxOjQpLCB0eXBlPSduJykKcG9pbnRzKG1fcGFyLCBjb2w9J3JlZCcpCmxpbmVzKGxfcGFyLCBjb2w9J2dyZWVuJykKYGBgCgpgYGB7cn0KcHJlZGljdChjbHVzdC5wYXJfenNjb3JlLG5ld2RhdGE9dW5saXN0KHpzY29yZShtMyRnbHVjb3NlVmFsdWUpKSkKcHJlZGljdChjbHVzdC5wYXJfenNjb3JlLG5ld2RhdGE9dW5saXN0KHpzY29yZShtNiRnbHVjb3NlVmFsdWUpKSkKcHJlZGljdChjbHVzdC5wYXJfenNjb3JlLG5ld2RhdGE9dW5saXN0KHpzY29yZShtOSRnbHVjb3NlVmFsdWUpKSkKcHJlZGljdChjbHVzdC5wYXJfenNjb3JlLG5ld2RhdGE9dW5saXN0KHpzY29yZShtMTIkZ2x1Y29zZVZhbHVlKSkpCmBgYAoKYGBge3J9CmluZGV4X3Bhcl96c2NvcmU9YXJhbmRpKGxfcGFyLG1fcGFyKQp1bmFkanVzdGVkX3Bhcl96c2NvcmU9YXJhbmRpKGxfcGFyLG1fcGFyLGFkanVzdD1GQUxTRSkKaW5kZXhfcGFyX3pzY29yZQp1bmFkanVzdGVkX3Bhcl96c2NvcmUKYGBgCgoKPT09PT09PT09PSBLIE1lYW5zID0+IFpzY29yZSA9PT09PT09PT09PQpgYGB7cn0Ka21lYW5zX2NsdXN0ZXJfenNjb3JlID0gS01lYW5zX3JjcHAodHJhaW5pbmdfZnJhbWVfenNjb3JlLGNsdXN0ZXJzPTQpCmxfa21lYW5zX3pzY29yZSA9IGttZWFuc19jbHVzdGVyX3pzY29yZSRjbHVzdGVyCmxfa21lYW5zX3pzY29yZQptX2ttZWFuc196c2NvcmUgPC0gYygyLDIsMiwzLDMsMyw0LDQsNCwxLDEsMSkKYGBgCgpgYGB7cn0KaW5kZXhfa21lYW5zX3pzY29yZSA9IGFyYW5kaShsX2ttZWFuc19zY2FsZWQsbV9rbWVhbnNfc2NhbGVkKQp1bmFkanVzdGVkX2ttZWFuc196c2NvcmUgPSBhcmFuZGkobF9rbWVhbnNfc2NhbGVkLG1fa21lYW5zX3NjYWxlZCxhZGp1c3QgPSBUUlVFKQppbmRleF9rbWVhbnNfenNjb3JlCnVuYWRqdXN0ZWRfa21lYW5zX3pzY29yZQpgYGAKCgo9PT09PT09PT1QbG90dGluZz09PT09PT09PT09PQpgYGB7cn0KY29sb3JzID0gYygncmVkJywnYmx1ZScsJ2dyZWVuJykKY190eXBlcyA9IGMoJ0hpZXJhcmNoaWNhbCBDbHVzdGVyaW5nJywnUGFydGl0aW9uYWwgQ2x1c3RlcmluZycsJ0sgTWVhbnMgQ2x1c3RlcmluZycpCnRfdHlwZXMgPSBjKCdSYXcnLCAnTWluLU1heCBTY2FsaW5nJywnWi1TY29yZSBOb3JtYWxpemF0aW9uJykKYGBgCgpgYGB7cn0KaW5kZXhfYWxsID0gYyhpbmRleF9oaWVyX3JhdyxpbmRleF9oaWVyX3NjYWxlZCxpbmRleF9oaWVyX3pzY29yZSwgaW5kZXhfcGFyX3JhdywgaW5kZXhfcGFyX3NjYWxlZCwgaW5kZXhfcGFyX3pzY29yZSwgaW5kZXhfa21lYW5zX3JhdywgaW5kZXhfa21lYW5zX3NjYWxlZCwgaW5kZXhfa21lYW5zX3pzY29yZSkKCnBsb3QoaW5kZXhfYWxsLHhheHQ9J24nLHR5cGU9J2gnLCBjb2wgPSBjb2xvcnMsIHhsYWI9IkNsdXN0ZXJpbmcgTWV0aG9kcyIsIHlsYWI9IkFkanVzdGVkIFJhbmRvbSBJbmRleCIsIG1haW49J0FkanVzdGVkIFJhbmRvbSBJbmRleDogSGllcmFyY2hpY2FsLCBQYXJ0aXRpb25hbCAmIEstTWVhbnMgQ2x1c3RlcmluZycsIGx3ZD00KQpheGlzKDEsIGF0PXNlcSgyLDksYnk9MyksIGxhYmVscz1jX3R5cGVzWzE6M10pCmxlZ2VuZCgidG9wcmlnaHQiLHRfdHlwZXMsIGNvbCA9IGNvbG9ycywgdGl0bGUgPSAnVHJhbnNmb3JtYXRpb24gVHlwZXMnLCBsd2Q9MixjZXg9Ljc1KQpgYGAKCmBgYHtyfQp1bmFkanVzdGVkX2FsbCA9IGModW5hZGp1c3RlZF9oaWVyX3JhdywgdW5hZGp1c3RlZF9oaWVyX3NjYWxlZCwgdW5hZGp1c3RlZF9oaWVyX3pzY29yZSwgdW5hZGp1c3RlZF9wYXJfcmF3LCB1bmFkanVzdGVkX3Bhcl9zY2FsZWQsIHVuYWRqdXN0ZWRfcGFyX3pzY29yZSwgdW5hZGp1c3RlZF9rbWVhbnNfcmF3LCB1bmFkanVzdGVkX2ttZWFuc19zY2FsZWQsIHVuYWRqdXN0ZWRfa21lYW5zX3pzY29yZSkKCnBsb3QodW5hZGp1c3RlZF9hbGwseGF4dD0nbicsdHlwZT0naCcsIGNvbCA9IGNvbG9ycywgeGxhYj0iQ2x1c3RlcmluZyBNZXRob2RzIiwgeWxhYj0iVW5hZGp1c3RlZCBSYW5kb20gSW5kZXgiLCBtYWluPSdVbmFkanVzdGVkIEluZGV4OiBIaWVyYXJjaGljYWwsIFBhcnRpdGlvbmFsICYgSy1NZWFucyBDbHVzdGVyaW5nJywgbHdkPTQpCmF4aXMoMSwgYXQ9c2VxKDIsOSxieT0zKSwgbGFiZWxzPWNfdHlwZXNbMTozXSkKbGVnZW5kKCJ0b3ByaWdodCIsdF90eXBlcywgY29sID0gY29sb3JzLCB0aXRsZSA9ICdUcmFuc2Zvcm1hdGlvbiBUeXBlcycsIGx3ZD0yLCBjZXg9MC43NSkKYGBgCgoK